package com.hex.ds.code.detection.oracle.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.hex.ds.code.detection.common.enums.AntlrEnum;
import com.hex.ds.code.detection.common.utils.AntlrUtil;
import com.hex.ds.code.detection.common.utils.SqlFormatterUtil;
import com.hex.ds.code.detection.oracle.gen.PlSqlLexer;
import com.hex.ds.code.detection.oracle.gen.PlSqlParser;
import com.hex.ds.code.detection.oracle.gen.PlSqlParserBaseVisitor;
import lombok.extern.slf4j.Slf4j;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;

@Slf4j
public class OracleTableParamSqlVisitor extends PlSqlParserBaseVisitor<String> {

    public static final String MARK_BEGIN = "/*";
    public static final String MARK_END = "*/";
    public static final String MARK_CODE = "[CODE]";
    public static final String ERROR_CODE = "[ERROR]";
    public static final String WARN_CODE = "[WARN]";
    public static final String ERROR_MARK_BEGIN = StrUtil.format(" {} {} {}", MARK_BEGIN, MARK_CODE, ERROR_CODE);
    public static final String WARN_MARK_BEGIN = StrUtil.format(" {} {} {}", MARK_BEGIN, MARK_CODE, WARN_CODE);

    // 语法解析异常标识 (遇到一次解析异常之后则打标记)
    private boolean isAnalyticException = false;

    public boolean getIsAnalyticException() {
        return isAnalyticException;
    }

    private String fileName = "";
    // 动态配置文件信息
//    private Map<String, Map<String, String>> config = new HashMap<>();
//    private Map<String, Map<String, Object>> objectConfig = new HashMap<>();
    private Map<String,String> config = new HashMap<>();
    private Boolean result = true;
    public Boolean getResult() { return result; }

//    public String visitRoot(String fileName, final String sourceSql, Map<String, Map<String, String>> config, Map<String, Map<String, Object>> objectConfig) {
public String visitRoot(String fileName, final String sourceSql, Map<String,String> config) {
        try {
            final CodePointCharStream input = CharStreams.fromString(sourceSql);
            this.fileName = fileName;
            this.config = config;
//            this.objectConfig = objectConfig;
            PlSqlLexer lexer = new PlSqlLexer(input);
            CommonTokenStream token = new CommonTokenStream(lexer);
            PlSqlParser parser = new PlSqlParser(token);
            String resultStr = this.visit(parser.sql_script());
            return SqlFormatterUtil.formatterSqlWithSpace(resultStr);
        } catch (Exception e) {
            log.info("推送下载稽核Value中语句不是Oracle的，转为Impala校验");
            throw new RuntimeException("推送下载稽核Value中语句不是Oracle的，转为Impala校验");
        }
    }

    @Override
    public String visitSubquery_basic_elements(PlSqlParser.Subquery_basic_elementsContext ctx) {
        try {
            // 若查询块在忽略SQL配置中 则跳过处理
//            List<String> ignoreSql = JsonReadUtil.castList(objectConfig.get("ignoreSql"), String.class);
            List<String> ignoreSql = getParamListUtil(config,"ignoreSql");
            List<String> compactSql = CollectionUtil.isEmpty(ignoreSql) ? null : ignoreSql.stream().map(s -> StringUtils.replaceChars(s, " \n\r\t", "")).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(compactSql) && CollectionUtil.contains(compactSql, ctx.getText())) {
                return "";
            } else {
                return super.visitSubquery_basic_elements(ctx);
            }
        } catch (Exception e) {
            log.info("【antlr解析异常】visitSubquery_basic_elements,推送下载稽核Value中语句不是Oracle的，转为Impala校验");
            throw new RuntimeException("【antlr解析异常】visitSubquery_basic_elements,推送下载稽核Value中语句不是Oracle的，转为Impala校验");
        }
    }

    @Override
    public String visitTableview_name(PlSqlParser.Tableview_nameContext ctx) {
        try {
            String resultStr = super.visitTableview_name(ctx);
            // 排除其父节点是Select_list_elementsContext的情况
            boolean exclude = AntlrUtil.queryDesignateParent(ctx, PlSqlParser.Select_list_elementsContext.class);
            // 库名存在且存在.号(库名.表名)结构
            if (ctx.identifier() != null && !exclude && ctx.PERIOD() != null) {
                List<String> paramsList = getServiceParam(fileName);
                if (CollectionUtil.isNotEmpty(paramsList) && !paramsList.contains(ctx.identifier().getText())) {
                    // 若都不存在，设置为错
                    this.result = false;
                    resultStr = resultStr + getMarkResult(AntlrEnum.VALUE_DB_NAME.getDesc(), "necessary");
                }
            }
            return resultStr;
        } catch (Exception e) {
            log.info("【antlr解析异常】visitTableview_name,推送下载稽核Value中语句不是Oracle的，转为Impala校验");
            throw new RuntimeException("【antlr解析异常】visitTableview_name,推送下载稽核Value中语句不是Oracle的，转为Impala校验");
        }
    }

    @Override
    public String visitTerminal(TerminalNode node) {
        if (!node.getText().equals("<EOF>")) {
            return node.getText();
        }
        return super.visitTerminal(node);
    }

    @Override
    public String visitErrorNode(ErrorNode node) {
        // 错误标记只打一次(因为语法错误会导致后续行多处解析异常)
        if (!isAnalyticException) {
            isAnalyticException = true;
            String resultStr = node.getText();
            return getMarkResult(AntlrEnum.ERROR_SQL.getDesc(), "necessary") + resultStr;
        } else {
            return node.getText();
        }
    }

    private List<String> getServiceParam(String tableName) {
        switch (tableName.toUpperCase()) {
            case "CTL_LOAD_CONFIG":
//                return JsonReadUtil.castList(objectConfig.get("serviceParam_ctlLoadConfig"), String.class);
                return getParamListUtil(config,"serviceParam_ctlLoadConfig");
            case "CTL_AUDIT_PARAM":
            case "CTL_AUDIT_PARAM_NEW":
//                return JsonReadUtil.castList(objectConfig.get("serviceParam_ctlAuditParam"), String.class);
            return getParamListUtil(config,"serviceParam_ctlAuditParam");
            case "TPUSHLIST":
//                return JsonReadUtil.castList(objectConfig.get("serviceParam_tpushlist"), String.class);
            return getParamListUtil(config,"serviceParam_tpushlist");
            default:
                return null;
        }
    }

    /**
     * @Method getMarkResult
     * @Param
     * @param scanName
     * @param scanLevel
     * @Return java.lang.String
     * @Description 获取SQL中的不同等级异常标记信息
     * @Author Wang zhihao
     * @Date 2024/2/6 13:33
     * @Version V1.0
     */
    private String getMarkResult(String scanName, String scanLevel) {
        switch (scanLevel.toUpperCase(Locale.ROOT)) {
            case "WARNING":
                return StrUtil.format("{} {} {}", WARN_MARK_BEGIN, scanName, MARK_END);
            case "NECESSARY":
                return StrUtil.format("{} {} {}", ERROR_MARK_BEGIN, scanName, MARK_END);
            default:
                return "";
        }
    }


    /**
     * @Package com.hex.ds.code.detection.component.code.detection.service
     * @ClassName OracleTableParamSqlVisitor
     * @Description 根据beforeParam对多参数进行切割返回List集合
     * @Author le.zhang
     * @Date 2024/3/4 10:02
     * @Version v1.0
     **/
    public List<String> getParamListUtil(Map<String,String> paramsMap,String beforeParam){
        String s = paramsMap.get(beforeParam);
        String[] split = s.split(",");
        List<String> resultList = Arrays.asList(split);
        List<String> list = new ArrayList<>();
        for (int i = 0;i<resultList.size();i++){
            list.add(resultList.get(i).trim());
        }
        return list;
    }
}
